package com.fang.system.rpc.pool;

import com.fang.system.rpc.handler.ServerDecodeHandler;
import com.fang.system.rpc.handler.ClientResponse;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.net.InetSocketAddress;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author fanglingxiao
 * @version 1.0
 * @description TODO
 * @date 2022/7/16 5:31 下午
 **/
public class ClientFactory {
    /**
     * 定义clientPool的size
     */
    private static final Integer POOL_SIZE = 1;
    /**
     * 单例的clientFactory
     */
    private static volatile ClientFactory clientFactory;
    /**
     * 获取连接池的随机算法
     */
    private static final Random RANDOM = new Random();
    /**
     * 创建服务端对应客户端的连接池group
     */
    private NioEventLoopGroup clientWorker;

    private final ConcurrentHashMap<InetSocketAddress, ClientPool> outBox = new ConcurrentHashMap<>();

    public static ClientFactory getInstance() {
        if (Objects.isNull(clientFactory)) {
            synchronized (ClientFactory.class) {
                if (Objects.isNull(clientFactory)) {
                    clientFactory = new ClientFactory();
                }
            }
        }
        return clientFactory;
    }

    public synchronized NioSocketChannel getClient(InetSocketAddress address) {
        if (Objects.isNull(address)) {
            throw new IllegalArgumentException("address is a null value");
        }
        ClientPool clientPool = outBox.get(address);
        if (Objects.isNull(clientPool)) {
            outBox.putIfAbsent(address, new ClientPool(POOL_SIZE));
            clientPool = outBox.get(address);
        }
        int index = RANDOM.nextInt(POOL_SIZE);
        NioSocketChannel client = clientPool.clients[index];
        if (Objects.nonNull(client) && client.isActive()) {
            return client;
        }
        return clientPool.clients[index] = createClient(address);
    }

    private NioSocketChannel createClient(InetSocketAddress address) {
        // 基于netty的客户端创建
        clientWorker = new NioEventLoopGroup(1);
        Bootstrap bootstrap = new Bootstrap();
        ChannelFuture connect = bootstrap.group(clientWorker)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        // 编解码
                        pipeline.addLast(new ServerDecodeHandler());
                        pipeline.addLast(new ClientResponse());
                    }
                })
                .connect(address);
        try {
            return (NioSocketChannel) connect.sync().channel();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        throw new RuntimeException("create client error");
    }
}
